#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

#include "psd_conn.h"
#include "psd_conn_file.h"
#include "psd_event.h"

static psdConn_fileData *psd_alloc_conn_filedata(int fd);
static int psd_conn_file_write(psdConn *sink, const void *buf, size_t len, int flags);
static int psd_conn_file_read(psdConn *src, void *buf, size_t len, int flags);
static int psd_conn_file_shutdown(psdConn *conn, uint8_t direction);
static int psd_conn_file_setbufsize(psdConn *conn, uint8_t direction, int size);
static int psd_conn_file_settimeout(psdConn *conn, uint8_t direction, int seconds);
static void psd_conn_file_free_file_data(psdConn *conn);

psdConn *psd_alloc_conn_file(int fd) {
	psdConn *new_conn;

	new_conn = psd_conn_alloc();
	if (!new_conn)
		goto error_exit;

	new_conn->conn_private = psd_alloc_conn_filedata(fd);
	if (!new_conn->conn_private)
		goto error_exit2;

	new_conn->stats_private = psd_conn_stats_alloc_def();
	if (!new_conn->stats_private)
		goto error_exit3;

	new_conn->protocol = "FILE";

	new_conn->read2 = psd_conn_file_read;
	new_conn->write2 = psd_conn_file_write;
	new_conn->shutdown2 = psd_conn_file_shutdown;
	new_conn->setbufsize2 = psd_conn_file_setbufsize;
	new_conn->settimeout2 = psd_conn_file_settimeout;
	new_conn->free_conn_private2 = psd_conn_file_free_file_data;
	new_conn->get_msg2 = psd_conn_default_get_msg;
	new_conn->send_msg2 = psd_conn_default_send_msg;
	new_conn->get_stat2 = psd_conn_default_get_stat;

	return new_conn;

error_exit3:
	free(new_conn->conn_private);
error_exit2:
	free(new_conn);
error_exit:
	return NULL;
}

psdConn_fileData *psd_alloc_conn_filedata(int fd) {
	psdConn_fileData *new_filedata;

	new_filedata = (psdConn_fileData *) malloc(sizeof(psdConn_fileData));
	if (!new_filedata)
		goto error_exit;

	if (pthread_mutex_init(&(new_filedata->lock), 0) < 0) {
		goto error_exit2;
	}
					
	new_filedata->fd = fd;
	new_filedata->closed = 0;

	return new_filedata;

error_exit2:
	free(new_filedata);
error_exit:
	return NULL;
}

static int psd_conn_file_write(psdConn *sink, const void *buf, size_t len, int flags) {
	int n;
	psdConn_fileData *file_data = (psdConn_fileData *) sink->conn_private;

	n = write(file_data->fd, buf, len);
	if (n > 0) 
		((psdConn_defStats *)sink->stats_private)->bytes_written += len;

	return n;
}

static int psd_conn_file_read(psdConn *src, void *buf, size_t len, int flags) {
	int n;
	psdConn_fileData *file_data = (psdConn_fileData *) src->conn_private;

	n = read(file_data->fd, buf, len);
	if (n > 0) 
		((psdConn_defStats *)src->stats_private)->bytes_read += len;

	return n;
}

static int psd_conn_file_shutdown(psdConn *conn, uint8_t direction) {
	psdConn_fileData *file_data = (psdConn_fileData *) conn->conn_private;

	pthread_mutex_lock(&(file_data->lock));
	{
		file_data->closed |= direction;
		if (file_data->closed == (PSD_SEND_SIDE | PSD_RECV_SIDE)) {
			gettimeofday(&(conn->end_time), NULL);
			psd_event("phoebus.connection.end", conn, "DEPOT=%s PROTOCOL=%s", conn->description, conn->protocol);
			close(file_data->fd);
		}
	}
	pthread_mutex_unlock(&(file_data->lock));

	return 0;
}

static int psd_conn_file_setbufsize(psdConn *conn, uint8_t direction, int size) {
	return size;
}

static int psd_conn_file_settimeout(psdConn *conn, uint8_t direction, int seconds) {
	return seconds;
}

static void psd_conn_file_free_file_data(psdConn *conn) {
	psdConn_fileData *file_data = conn->conn_private;
	pthread_mutex_destroy(&file_data->lock);
	free(file_data);
}
